package sdcncsi.ict.counter;

import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

public class CounterService {
	//日志记录项
	private static final Log logger = LogFactory.getLog(CounterService.class);
	//计数器应用列表
	private static final HashMap<Integer,CounterApp> counterAppList = new HashMap<Integer,CounterApp>();
	static{
		try{
			List appList = DBTool.getJdbcTemplate().queryForList("select * from t_counter_app where zt_dm=1");
			Iterator itr = appList.iterator();
			while(itr.hasNext()){
				//获得计数器应用
				Map counterApp = (Map)itr.next();
				Object tempObj = counterApp.get("jssjjg");
				int jssjjg = (tempObj==null)?-1:(Integer)tempObj;
				tempObj = counterApp.get("jlos");
				int jlos = (tempObj==null)?0:(Integer)tempObj;
				tempObj = counterApp.get("jlllq");
				int jlllq = (tempObj==null)?0:(Integer)tempObj;
				//构造CounterApp结构
				CounterApp ca = new CounterApp();
				ca.id = (Integer)counterApp.get("id");//一定不为空
				ca.name = (String)counterApp.get("mc");
				ca.host = (String)counterApp.get("host");
				ca.ip = (String)counterApp.get("ip");
				ca.interval = jssjjg;
				ca.logOS = (jlos==1);
				ca.logBrowser = (jlllq==1);
				counterAppList.put(ca.id, ca);
			}
		}catch (DataAccessException e) {}
	}
	//private static boolean uvMutex = false;//更新访客锁
	//计数器常量
	public static final String COOKIE_NAME = "counter_net_uv";//计数器cookie名称
	public static final int MAX_CACHES = 3000;	//最大缓存计数访问项个数
	public static final int APP_COUNTERS = 300;	//为每个应用缓存计数器的个数
	public static final int COUNTER_DAYS = 31;	//为每个计数器记录数值的天数
	public static final int WRITEDB_INTERVAL = 30000;//写数据库时间间隔（单位：ms）
	
	/**
	 * 获得appID的计数应用配置
	 */
	public static CounterApp getCounterApp(int appID){
		CounterApp ca = (CounterApp)counterAppList.get(appID);
		return ca;
	}
	
	/**
	 * 得到计数器统计数值
	 * @param type 显示的类型 PV、IP、UV、Session
	 */
	public Map getCurrentCount(String type, int appID, String counterID){
		Map dataMap = new HashMap();
		dataMap.put("result", true);
		//从内存获得统计数值缓存
		CounterNumber cn = CounterCache.getCunnterNumber(appID, counterID);
		
		//获得计数类型
		int typeInt = 0;
		try{
			typeInt = Integer.parseInt(type);
		}catch (Exception e) {}
		
		//获得计数值
		if(cn!=null){
			switch (typeInt) {
			case 0://pv数值（page view）
				dataMap.put("count", cn.getPV());
				break;
			case 1://ip数值（IP来源）
				dataMap.put("count", cn.getIP());
				break;
			case 2://uv数值（访问者计数）
				dataMap.put("count", cn.getUV());
				break;
			case 3://seesion数值（回话计数）
				dataMap.put("count", cn.getSS());
				break;
			default://pv数值（page view）
				dataMap.put("count", cn.getPV());
				break;
			}
		}else{//非法的计数器ID
			dataMap.put("result", false);
		}
		return dataMap;
	}
	/**
	 * 更新统计数值
	 */
	public boolean updateCount(Map<String,String> requestMap) {
		//得到引用页
		String referer = requestMap.get("referer");
		if (referer == null) {//引用页为null不更新计数
			return false;
		}
		//应用ID
		int appID=1;//默认计数器应用ID为1
		String appIDStr = requestMap.get("appid");
		try{
			appID = Integer.parseInt(appIDStr);
		}catch (NumberFormatException e) { }
		//根据应用ID获得计数器应用配置
		CounterApp ca = (CounterApp)counterAppList.get(appID);
		//计数器ID
		String counterID = requestMap.get("id");
		if(counterID==null){counterID = "1";}//默认计数器ID为1
		
		//获得计数器统计数值类结构
		if(CounterCache.getOrAddCunnterNumber(appID, counterID)==null){
			logger.error("CounterService..updateCount():获得计数器"+appID+"->"+counterID+"->"+"统计数值结构CounterNumber错误。无效的应用ID");
			return false;
		}
		
		//访客标识uv值以cookie(COOKIE_NAME)值记录
		String uv = requestMap.get(CounterService.COOKIE_NAME);
		if(uv!=null){
			//判断是否是频繁访问
			VisitItem vi = CounterCache.getGlobalCache(appID, counterID, uv);
			if(vi!=null){
				int jssjjg = ca.interval;
				if(jssjjg>0){//计数时间间隔大于0时起作用
					long nowTimestamp = System.currentTimeMillis();
					long lastVisitTime = vi.getTimestamp();
					vi.setTimestamp(nowTimestamp);
					if(nowTimestamp-lastVisitTime<jssjjg){
						return true;
					}
				}
			}
		}else{
			uv = generateVisitGuid();
			requestMap.put(CounterService.COOKIE_NAME, uv);//放到map，为CounterController.updateCount(),中会写cookie
		}
		
		//1.1更新访客UV访问记录数值
		if(CounterCache.getGlobalCache(appID, counterID, uv)==null || getUV(appID, counterID)==0){
			VisitItem vi = new VisitItem();
			CounterCache.putGlobalCache(appID, counterID, uv, vi);
			incUV(appID, counterID);
		}
		//1.2更新IP访问记录数值
		String ip = requestMap.get("ip");//访问来源IP地址
		if(CounterCache.getGlobalCache(appID, counterID, ip)==null || getIP(appID, counterID)==0){
			VisitItem vi = new VisitItem();
			CounterCache.putGlobalCache(appID, counterID, ip, vi);
			incIP(appID, counterID);
		}
		//1.3更新session数值
		String session = requestMap.get("session");//当前会话session id
		String req_session = requestMap.get("req_seesion");//请求来源session
		if(req_session==null || !req_session.equals(session) || getSS(appID, counterID)==0){
			incSS(appID, counterID);
		}
		//1.4更新PageView浏览量
		incPV(appID, counterID);

		//2.1更新referer访问记录
		if(CounterCache.getGlobalCache(appID, counterID, referer)==null){
			VisitItem vi = new VisitItem();
			CounterCache.putGlobalCache(appID, counterID, referer, vi);
		}
		
		//3.1更新操作系统记录
		String user_agent = requestMap.get("user-agent");
		String os = getClientOS(user_agent);//操作系统字符串
		if(ca.logOS){
			//更新OS访问记录
			if(CounterCache.getGlobalCache(appID, counterID, os)==null){
				VisitItem vi = new VisitItem();
				CounterCache.putGlobalCache(appID, counterID, os, vi);
			}
		}
		//3.2更新浏览器记录
		String browser = getClientBrowser(user_agent);//浏览器字符串："浏览器名:版本号"
		if(ca.logBrowser){
			//更新OS访问记录
			if(CounterCache.getGlobalCache(appID, counterID, browser)==null){
				VisitItem vi = new VisitItem();
				CounterCache.putGlobalCache(appID, counterID, browser, vi);
			}
		}
		//把访问记录放到VisitRecord cache
		VisitRecord newVisitRecord = new VisitRecord(appID,counterID,referer,ip,uv,os,browser);
		String visitGuid = generateVisitGuid();
		CounterCache.addVisitRecord( visitGuid, newVisitRecord);
		//写日志
		//logger.info(newVisitRecord.toString());
		return true;
	}
	private int getUV(int appID, String counterID){
		return CounterCache.getOrAddCunnterNumber(appID, counterID).getUV();
	}
	private int getIP(int appID, String counterID){
		return CounterCache.getOrAddCunnterNumber(appID, counterID).getIP();
	}
	private int getSS(int appID, String counterID){
		return CounterCache.getOrAddCunnterNumber(appID, counterID).getSS();
	}
//	private int getPV(int appID, String counterID){
//		return CounterCache.getOrAddCunnterNumber(appID, counterID).getPV();
//	}
	private void incUV(int appID, String counterID){
		CounterCache.getOrAddCunnterNumber(appID, counterID).incUV();
	}
	private void incIP(int appID, String counterID){
		CounterCache.getOrAddCunnterNumber(appID, counterID).incIP();
	}
	private void incSS(int appID, String counterID){
		CounterCache.getOrAddCunnterNumber(appID, counterID).incSS();
	}
	private void incPV(int appID, String counterID){
		CounterCache.getOrAddCunnterNumber(appID, counterID).incPV();
	}
	
	//格式化时间 String 日期+时间
	private static String formateDatetime(Date date) {
		java.text.DateFormat format1 = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return format1.format(date);
	}
	
	/**
	 * 获得OS(操作系统)字符串,从浏览器的user-agent属性获得
	 */
	private String getClientOS(String user_agent) {
		String os = "未知操作系统";
		if (user_agent == null)
			return os;
		// 检索操作系统描述字符串
		int p1, p2 = -1;
		p1 = user_agent.indexOf("(");
		if (p1 != -1) {
			p2 = user_agent.indexOf(")", p1 + 1);
		}
		if (p1 != -1 && p2 != -1) {
			// 获得操作系统描述字符串
			String os_str = user_agent.substring(p1 + 1, p2);
			String[] temp_arr = os_str.split(";");
			if (temp_arr.length > 2) {// 必须为3个或3个以上字符串
				os = temp_arr[2];//一般为第3个字符串
				if(os.indexOf("Win")==-1 && os.indexOf("Mac")==-1 && os.indexOf("Linux")==-1 && os.indexOf("X11")==-1){
					os = temp_arr[0];//其次为第1个字符串
				}
				if(os.indexOf("Win")==-1 && os.indexOf("Mac")==-1 && os.indexOf("Linux")==-1 && os.indexOf("X11")==-1){
					os = temp_arr[1];//最后探索为第2个字符串
				}
				if(os.indexOf("Win")==-1 && os.indexOf("Mac")==-1 && os.indexOf("Linux")==-1 && os.indexOf("X11")==-1){
					os = "未知操作系统";
				}else{
					os = os.trim();// 去除空格
					os = os.replace('_', '.');// 把下划线'_'换成点'.'
				}
			}
		}
		return os;
	}
	
	/**
	 * 获得Browser(浏览器)字符串,从浏览器的user-agent属性获得
	 */
	private String getClientBrowser(String user_agent) {
		String browser = "未知浏览器";
		if (user_agent == null)
			return browser;
		// 转化“浏览器表头”为小写
		String ua_lower = user_agent.toLowerCase();
		// 各个浏览器的“正则样式”
		Pattern ie = Pattern.compile("msie ([\\d.]+)");// IE
		Pattern firefox = Pattern.compile("firefox\\/([\\d.]+)");// 火狐
		Pattern chrome = Pattern.compile("chrome\\/([\\d.]+)");// 谷歌
		Pattern opera = Pattern.compile("opera.([\\d.]+)");// opera
		Pattern safari = Pattern.compile("version\\/([\\d.]+).*safari");// safari
		Pattern maxthon = Pattern.compile("maxthon\\/([\\d.]+)");// 傲游
		// 判断
		boolean finded = false;
		if (!finded) {// 1)IE
			Matcher matcher = ie.matcher(ua_lower);
			if (matcher.find()) {
				browser = "IE:" + matcher.group(1);
				finded = true;
			}
		}
		if (!finded) {// 2)火狐
			Matcher matcher = firefox.matcher(ua_lower);
			if (matcher.find()) {
				browser = "Firefox:" + matcher.group(1);
				finded = true;
			}
		}
		if (!finded) {// 3)谷歌
			Matcher matcher = chrome.matcher(ua_lower);
			if (matcher.find()) {
				browser = "Chrome:" + matcher.group(1);
				finded = true;
			}
		}
		if (!finded) {// 4)opera
			Matcher matcher = opera.matcher(ua_lower);
			if (matcher.find()) {
				browser = "Opera:" + matcher.group(1);
				finded = true;
			}
		}
		if (!finded) {// 5)safari
			Matcher matcher = safari.matcher(ua_lower);
			if (matcher.find()) {
				browser = "Safari:" + matcher.group(1);
				finded = true;
			}
		}
		if (!finded) {// 6)傲游
			Matcher matcher = maxthon.matcher(ua_lower);
			if (matcher.find()) {
				browser = "Maxthon:" + matcher.group(1);
				finded = true;
			}
		}
		return browser;
	}
	
	/**
	 * 注册用户
	 * @param paramMap
	 * @return
	 */
	public static Map userRegister(Map<String, String> paramMap){
		Map resultMap = new HashMap();
		resultMap.put("result", true);
		String xm = paramMap.get("xm");
		try {
			xm = new String(xm.getBytes("ISO-8859-1"), "UTF-8");
		} catch (UnsupportedEncodingException e2) {
			e2.printStackTrace();
		}
		String dlmc = paramMap.get("dlmc");
		String dlmm = paramMap.get("dlmm");
		String dlmm2 = paramMap.get("dlmm2");
		String dzyx = paramMap.get("dzyx");
		String now_str = formateDatetime(new Date());// 当前时间字符串
		if(!dlmm.equals(dlmm2)){//再次检测两次输入密码，javascript已经检测
			resultMap.put("result",false);
			resultMap.put("msg", "请核对密码!");
			return resultMap;
		}
		try{
			if(isUserExist(dlmc)){//检测登录名是否已存在
				resultMap.put("result", false);
				resultMap.put("msg", "登录名已被占用");
				return resultMap;
			}
			// 连接数据库准备
			JdbcTemplate jdbcTemplate = DBTool.getJdbcTemplate();
			String sql = "insert into t_counter_user(xm,dlmc,dlmm,dzyx,zt_dm,cjsj)" +
					" values('"+xm+"','"+dlmc+"','"+dlmm+"','"+dzyx+"',1,'"+now_str+"')";
			KeyHolder keyHolder = new GeneratedKeyHolder();
			jdbcTemplate.update(new GenKeyPreStatement(sql), keyHolder);
			int user_id = keyHolder.getKey().intValue();
			resultMap.put("id", user_id);
			resultMap.put("xm", xm);
			resultMap.put("dlmc", dlmc);
			resultMap.put("dzyx", dzyx);
		}catch(DataAccessException e){
			resultMap.put("result", false);
			resultMap.put("msg", "服务器忙，请稍后重试。");
			logger.error("[用户注册]连接错误！"+e.toString());
		}
		return resultMap;
	}
	
	/**
	 * 检测用户登录名是否被占用
	 * @param dlmc
	 * @return
	 */
	public static boolean isUserExist(String dlmc){
		// 连接数据库准备
		JdbcTemplate jdbcTemplate = DBTool.getJdbcTemplate();
		String sql = "select count(1) from t_counter_user where dlmc='"+dlmc+"' and zt_dm=1";
		int isUserExist = jdbcTemplate.queryForInt(sql);
		if(isUserExist>0){
			return true;
		}
		return false;
	}
	
	/**
	 * 注册登录
	 * @param paramMap
	 * @return
	 */
	public static Map userLogin(Map<String, String> paramMap){
		Map resultMap = new HashMap();
		resultMap.put("result", true);
		String dlmc = paramMap.get("dlmc");
		String dlmm = paramMap.get("dlmm");
		try{
			// 连接数据库准备
			JdbcTemplate jdbcTemplate = DBTool.getJdbcTemplate();
			String sql = "select * from t_counter_user where dlmc='"+dlmc+"' and dlmm='"+dlmm+"' and zt_dm=1";
			//查询用户信息
			Map userMap = jdbcTemplate.queryForMap(sql);
			resultMap.putAll(userMap);
			logger.info("[用户登录]姓名："+resultMap.get("xm")+",登录名："+resultMap.get("dlmc")+",电子邮箱："+resultMap.get("dzyx"));
		} catch (EmptyResultDataAccessException e) {
			resultMap.put("result", false);
			resultMap.put("msg", "用户名或密码错误！");
		} catch (IncorrectResultSizeDataAccessException e) {
			resultMap.put("result", false);
			resultMap.put("msg", "服务器忙，请重试。");
			logger.error("[用户登录]用户记录不唯一！"+e.toString());
		} catch (DataAccessException e) {
			resultMap.put("result", false);
			resultMap.put("msg", "服务器忙，请稍后重试。");
			logger.error("[用户登录]连接错误！"+e.toString());
		}
		return resultMap;
	}
	
	/**
	 * 获得某用户的网站计数器列表
	 * @param user_id
	 * @return <List> countersList
	 */
	public static List getUserCounters(int user_id){
		List countersList = null;
		try{
			JdbcTemplate jdbcTemplate = DBTool.getJdbcTemplate();
			String sql = "select * from t_counter where user_id="+user_id+" and zt_dm=1";
			countersList = jdbcTemplate.queryForList(sql);
		}catch(DataAccessException e){
			logger.error("获得用户计数器列表错误！"+e.toString());
		}
		return countersList;		
	}
	
	/**
	 * 生成访问唯一标识
	 * @return
	 */
	protected static String generateVisitGuid() {
		long time = System.currentTimeMillis();
		long nano = System.nanoTime();
		
		StringBuffer uvguid = new StringBuffer(50);
		//当前时间--毫秒
		String curTimeString = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date(time));
		uvguid.append(curTimeString);
		//当前时间--纳秒
		long nanoRemaind = nano%10000000;
		if(nanoRemaind<1000000){
			nanoRemaind+=1000000;
		}
		uvguid.append(nanoRemaind);
		//6位随机数
		int rand = (int)(Math.random() * 900000.0 + 100000.0);
		uvguid.append(rand);
		//return
		return uvguid.toString();
	}
	
	
	static class CounterApp{
		int id;
		int userID=-1;
		String name;
		String host;
		String ip;
		int interval=-1;
		boolean logOS=false;
		boolean logBrowser=false;
		//构造方法
		CounterApp(){
			
		}
		CounterApp(int id, int userID, String mc, String host, String ip, int jssjjg, boolean jlos, boolean jlllq) {
			super();
			this.id = id;
			this.userID = userID;
			this.name = mc;
			this.host = host;
			this.ip = ip;
			this.interval = jssjjg;
			this.logOS = jlos;
			this.logBrowser = jlllq;
		}
		
	}
}
